﻿using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Star.Tools.Excel.Extend
{
    public static class ObjectExtensions
    {
        private static readonly ConcurrentDictionary<Type, PropertyInfo[]> PropertiesDic =new ConcurrentDictionary<Type, PropertyInfo[]>();

        /// <summary>
        /// 获取实体字段
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static PropertyInfo[] GetEntityProperties(this Type type)
        {
            return PropertiesDic.GetOrAdd(type, item => type.GetProperties(BindingFlags.Instance | BindingFlags.Public));
        }
        public static PropertyInfo[] GetProperties<T>(this T entity)
        {
            return entity == null ? new PropertyInfo[0] : entity.GetType().GetProperties();
        }
        /// <summary>
        ///  var titles = typeof(T).GetProperties().Select(o => o.GetDescription()).ToArray();
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <returns></returns>
        public static string GetDescription(this PropertyInfo propertyInfo)
        {
            var attribute = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true).FirstOrDefault();
            return attribute == null ? propertyInfo.Name : ((DescriptionAttribute)attribute).Description;
        }

        public static dynamic GetValue<T>(this T entity, PropertyInfo propertyInfo)
        {
            return propertyInfo.GetValue(entity, null);
        }
        #region 实体复制

        /// <summary>
        /// 实现对象属性复制
        /// </summary>
        /// <param name="origin">原始对象</param>
        /// <param name="target">目标对象</param>
        public static void CopyValue(object origin, object target)
        {
            PropertyInfo[] properties = (target.GetType()).GetProperties();
            PropertyInfo[] fields = (origin.GetType()).GetProperties();
            for (int i = 0; i < fields.Length; i++)
            {
                for (int j = 0; j < properties.Length; j++)
                {
                    if (fields[i].Name == properties[j].Name && fields[i].PropertyType == properties[j].PropertyType && properties[j].CanWrite)
                    {
                        try
                        {
                            properties[j].SetValue(target, fields[i].GetValue(origin, null), null);
                        }
                        catch { }
                    }
                }
            }
        }

        /// <summary>
        /// 对象复制
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="origin">源对象</param>
        /// <param name="target">目标对象</param>
        /// <param name="copyFields">复制的字段集合</param>
        public static void CopyValue<T>(T origin, T target, List<string> copyFields = null) where T : class
        {
            if (origin == null || target == null) return;
            PropertyInfo[] properties = typeof(T).GetProperties().Where(x => x.CanWrite).ToArray();
            if (copyFields != null && copyFields.Count > 0)
                properties = properties.Where(x => copyFields.Contains(x.Name)).ToArray();
            foreach (PropertyInfo p in properties)
            {
                try
                {
                    p.SetValue(target, p.GetValue(origin, null), null);
                }
                catch { }
            }
        }

        #endregion
        #region # 对象属性赋值 
        /// <summary>
        /// 对象属性赋值
        /// </summary>
        /// <typeparam name="TSource">源实例类型</typeparam>
        /// <typeparam name="TTarget">目标实例类型</typeparam>
        /// <param name="sourceInstance">源实例</param>
        /// <param name="targetInstance">目标实例</param>
        public static void Fill<TSource, TTarget>(this TSource sourceInstance, TTarget targetInstance)
        {
            //01.获取源对象与目标对象的类型
            Type sourceType = sourceInstance.GetType();
            Type targetType = targetInstance.GetType();

            //02.获取源对象与目标对象的所有属性
            PropertyInfo[] sourceProps = sourceType.GetProperties();
            PropertyInfo[] targetProps = targetType.GetProperties();

            //03.双重遍历，判断属性名称是否相同，如果相同则赋值
            foreach (PropertyInfo tgtProp in targetProps)
            {
                foreach (PropertyInfo srcProp in sourceProps)
                {
                    if (tgtProp.Name == srcProp.Name)
                    {
                        tgtProp.SetValue(targetInstance, srcProp.GetValue(sourceInstance, null), null);
                    }
                }
            }
        }
        #endregion
        #region Map

        ///// <summary>
        ///// 浅克隆
        ///// </summary>
        ///// <param name="source">源</param>
        ///// <typeparam name="TDestination">目标类型</typeparam>
        ///// <returns>目标类型</returns>
        //public static TDestination Clone<TDestination>(this object source)
        //    where TDestination : new()
        //{
        //    TDestination dest = new TDestination();
        //    dest.GetType().GetProperties().ForEach(p =>
        //    {
        //        p.SetValue(dest, source.GetType().GetProperty(p.Name)?.GetValue(source));
        //    });
        //    return dest;
        //}

        /// <summary>
        /// 深克隆
        /// </summary>
        /// <param name="source">源</param>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <returns>目标类型</returns>
        public static TDestination DeepClone<TDestination>(this object source)
            where TDestination : new()
        {
            return JsonConvert.DeserializeObject<TDestination>(JsonConvert.SerializeObject(source));
        }

        ///// <summary>
        ///// 浅克隆(异步)
        ///// </summary>
        ///// <param name="source">源</param>
        ///// <typeparam name="TDestination">目标类型</typeparam>
        ///// <returns>目标类型</returns>
        //public static Task<TDestination> CloneAsync<TDestination>(this object source)
        //    where TDestination : new()
        //{
        //    return Task.Run(() =>
        //    {
        //        return source.Clone<TDestination>();
        //    });
        //}

        /// <summary>
        /// 深克隆(异步)
        /// </summary>
        /// <param name="source">源</param>
        /// <typeparam name="TDestination">目标类型</typeparam>
        /// <returns>目标类型</returns>
        public static async Task<TDestination> DeepCloneAsync<TDestination>(this object source)
            where TDestination : new()
        {
            return await Task.Run(() => JsonConvert.DeserializeObject<TDestination>(JsonConvert.SerializeObject(source)));
        }

        #endregion Map
    }
}
